package com.simplemobiletools.camera.document

import android.app.Activity
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Matrix
import android.graphics.Point
import android.icu.number.IntegerWidth
import android.media.ExifInterface
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.InputType
import android.util.Log
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.atom.module.document.utils.DocumentUtils
import com.atom.module.logger.Logger
import com.huawei.hmf.tasks.OnFailureListener
import com.huawei.hmf.tasks.OnSuccessListener
import com.huawei.hmf.tasks.Task
import com.huawei.hms.mlsdk.common.MLFrame
import com.huawei.hms.mlsdk.dsc.*
import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.document.view.CropImageView
import com.simplemobiletools.camera.utils.FileUtil
import io.reactivex.internal.observers.BlockingBaseObserver
import java.io.IOException
import java.util.*
import javax.vecmath.Vector2d
import kotlin.collections.ArrayList

/**
 * All rights Reserved, Designed By www.rongdasoft.com
 * @version V1.0
 * @Title: DocumentActivity
 * @Description:
 * @author: wuyongzheng
 * @date: 2022/3/18
 * @Copyright: 2022/3/18 www.rongdasoft.com Inc. All rights reserved.
 */
class DocumentActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_document_cut)
        imgCutResult = findViewById(R.id.imgCutResult)
        findViewById<View>(R.id.pickImage).setOnClickListener {
            pickImage()
        }
        findViewById<View>(R.id.photoImage).setOnClickListener {
            takePhoto()
        }
        findViewById<View>(R.id.reCutImage).setOnClickListener {
            findViewById<CropImageView>(R.id.ivCrop)?.also {
                if (it.canRightCrop()) {
                    setResult(it.cropPoints, MLFrame.fromBitmap(getCompressesBitmap))
                    it.cropPoints.forEachIndexed { index, point ->
                        Log.e(
                            "DocumentActivity",
                            "bitmap=[${it.bitmap.width} , ${it.bitmap.height}] , point=[$index ,$point]"
                        )
                    }
                }
            }
        }

        findViewById<View>(R.id.computePoint).setOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                createInputDialog { value ->
                    if (value < 0) return@createInputDialog
                    val it = findViewById<CropImageView>(R.id.ivCrop);
                    it.bitmap ?: return@createInputDialog
                    val w = it.bitmap.width
                    val h = it.bitmap.height
                    it.cropPoints ?: return@createInputDialog
                    val cropPoints = Arrays.copyOf(it.cropPoints, 4)
                    var idx = -1
                    var max = Double.MAX_VALUE
                    val degreeArray = DoubleArray(4)
                    val lineArray = DoubleArray(4)
                    cropPoints.forEachIndexed { index, point ->
                        val compute =
                            compute(
                                point,
                                cropPoints[(index + 1) % 4],
                                cropPoints[(index + 3) % 4]
                            )
                        degreeArray[index] = compute
                        val space = Math.abs(90.0 - compute)
                        if (space < max) {
                            max = space
                            idx = index
                        }
                    }

                    lineArray[0] = degreeArray[0] + degreeArray[1];
                    lineArray[1] = degreeArray[1] + degreeArray[2];
                    lineArray[2] = degreeArray[2] + degreeArray[3];
                    lineArray[3] = degreeArray[3] + degreeArray[0];
                    Logger.e("degreeArray degree0-> ${degreeArray[0]} degree1-> ${degreeArray[1]} degree2-> ${degreeArray[2]} degree3-> ${degreeArray[3]}")
                    Logger.e("lineArray line0-> ${lineArray[0]} line1-> ${lineArray[1]} line2-> ${lineArray[2]} line3-> ${lineArray[3]}")

                    var maxLine = Double.MAX_VALUE
                    var idxLine = -1
                    lineArray.forEachIndexed { index, d ->
                        val space = Math.abs(180.0 - d)
                        if (space < maxLine) {
                            maxLine = space
                            idxLine = index
                        }
                    }
                    Logger.e("lineArray maxLine-> ${maxLine} idxLine-> ${idxLine}")
                    if (maxLine <= value) {
                        Logger.e("lineArray maxLine-> 1")

                        var p1: Int = -1 // 长边角
                        var p2: Int = -1 // 短边角
                        if (Math.abs(degreeArray[idxLine] - degreeArray[(idxLine + 1) % 4]) <= 5) {
                            p1 = idxLine
                            p2 = (idxLine + 1) % 4
                        }

                        if (Math.abs(degreeArray[(idxLine + 2) % 4] - degreeArray[(idxLine + 3) % 4]) <= 5) {
                            p1 = (idxLine + 2) % 4
                            p2 = (idxLine + 3) % 4
                        }

                        if (p1 != -1 && p2 != -1) {
                            var line1 = getPointLine(cropPoints[(p1 + 2) % 4], cropPoints[p1]) // 长边
                            var line2 = getPointLine(cropPoints[(p2 + 2) % 4], cropPoints[p2])// 短边

                            if (getLineLengthSquared(line1) > getLineLengthSquared(line2)) {
                                // 不做任何
                            }

                            if (getLineLengthSquared(line1) < getLineLengthSquared(line2)) {
                                // 不做任何
                                var temp = p2
                                p2 = p1
                                p1 = temp

                                var temp1 = line2
                                line2 = line1
                                line1 = temp1
                            }

                            val pointToVector = pointToVector(cropPoints[p2])
                            val p = Vector2d().apply {
                                this.add(pointToVector, line1)
                            }
                            val pointLine = getPointLine(cropPoints[p1], cropPoints[p2])
                            val result = Vector2d().apply {
                                this.add(p, pointLine)
                                this.add(pointLine)
                            }
                            Logger.e(" result ${result}  ${(p2 + 2) % 4}")
                            cropPoints[(p2 + 2) % 4] = Point(result.x.toInt(), result.y.toInt())
                            //it.cropPoints = cropPoints
                            createViewDialog(it, cropPoints)
                            return@createInputDialog
                        }
                    }
                    Logger.e("lineArray 1")
                    // 判断对角是否小于180
                    var point1 = 0
                    var point2 = 2
                    if (degreeArray[point1] + degreeArray[point1] > 180) {
                        point1 = 1
                        point2 = 3
                    }

                    Logger.e("lineArray 2")
                    val v0 = pointToVector(cropPoints[point1])
                    val p1 = Point(cropPoints[point1].x, cropPoints[point2].y)
                    val v1 = pointToVector(p1)
                    val v2 = pointToVector(cropPoints[point2])

                    val pointLine02 = getPointLine(cropPoints[point2], cropPoints[point1])
                    val pointLine01 = getPointLine(p1, cropPoints[point1])
                    val pointLine03 = Vector2d().also {
                        //it.add(pointLine01, pointLine01)
                        it.add(pointLine01)
                    }

                    val pointLine3 = Vector2d().also {
                        it.add(v0, pointLine03)
                    }

                    val pointLine04 = Vector2d().also {
                        it.sub(pointLine02, pointLine03)
                    }

                    val pointLine5 = Vector2d().also {
                        it.add(v0, pointLine04)
                    }
                    cropPoints[(point1 + 1) % 4] = Point(pointLine5.x.toInt(), pointLine5.y.toInt())
                    cropPoints[(point2 + 1) % 4] = Point(pointLine3.x.toInt(), pointLine3.y.toInt())
                    createViewDialog(it, cropPoints)
                    return@createInputDialog
                }
            }
        })
        analyzer = createAnalyzer()
    }

    fun createInputDialog(block: (Double) -> Unit) {
        val builder = AlertDialog.Builder(this, R.style.live_editTextDialogStyle)
        val input = EditText(this)
        builder.setView(input)
        input.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED
        input.setText("5")
        input.setSelection(input.text.toString().length)
        builder.setPositiveButton("sure") { dialog, state ->
            val toString = input.text.toString()
            toString.toDoubleOrNull()?.also {
                block.invoke(it)
            }
            dialog.dismiss()
        }
        builder.setNegativeButton("cancel", { dialog, state ->

        })
        val show = builder.create()
        show.setOnShowListener {
            input.requestFocus()
            val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.showSoftInput(input, InputMethodManager.SHOW_FORCED)
        }
        show.show()
    }

    fun createViewDialog(view: CropImageView, points: Array<Point>) {
        val builder = AlertDialog.Builder(this)
        val input = CropImageView(this)
        builder.setView(input)
        builder.setNegativeButton("cancel") { dialog, state ->

        }
        val show = builder.create()
        show.setOnShowListener {
            input.setImageBitmap(view.bitmap)
            input.cropPoints = points
        }
        show.show()
    }

    fun getPointLine(pLeft: Point, pCenter: Point, pRight: Point): Vector2d {
        val v1 =
            Vector2d(pLeft.x.toDouble(), pLeft.y.toDouble())
        val v2 = Vector2d(
            pCenter.x.toDouble(),
            pCenter.y.toDouble()
        )
        val v3 = Vector2d(
            pRight.x.toDouble(),
            pRight.y.toDouble()
        )
        val lineLeft = Vector2d().apply {
            this.sub(v1, v2)
        }
        val lineRight = Vector2d().apply {
            this.sub(v3, v1)
        }
        val lineCenter = Vector2d().apply {
            this.add(lineLeft, lineRight)
        }
        return lineCenter
    }

    fun getPointLine(pStart: Point, pEnd: Point): Vector2d {
        val v1 =
            Vector2d(pStart.x.toDouble(), pStart.y.toDouble())
        val v2 = Vector2d(
            pEnd.x.toDouble(),
            pEnd.y.toDouble()
        )
        return Vector2d().apply {
            this.sub(v1, v2)
        }
    }

    fun getLineLengthSquared(v: Vector2d): Double {
        return v.lengthSquared()
    }

    fun pointToVector(p: Point): Vector2d {
        return Vector2d(p.x.toDouble(), p.y.toDouble())
    }


    fun compute(p0: Point, p01: Point, p02: Point): Double {
        val v1 = Vector2d(p0.x.toDouble(), p0.y.toDouble())
        val v2 = Vector2d(p01.x.toDouble(), p01.y.toDouble())
        val v3 = Vector2d(p02.x.toDouble(), p02.y.toDouble())

        val line1 = Vector2d().apply {
            this.sub(v2, v1)
            this.normalize()
        }
        val line2 = Vector2d().apply {
            this.sub(v3, v1)
            this.normalize()
        }
        return Math.toDegrees(line1.angle(line2))
    }


    private fun createAnalyzer(): MLDocumentSkewCorrectionAnalyzer? {
        val setting = MLDocumentSkewCorrectionAnalyzerSetting.Factory()
            .create()
        return MLDocumentSkewCorrectionAnalyzerFactory.getInstance()
            .getDocumentSkewCorrectionAnalyzer(setting)
    }

    private var imageUri: Uri? = null
    private var getCompressesBitmap: Bitmap? = null

    private fun takePhoto() {
        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        if (takePictureIntent.resolveActivity(this.packageManager) != null) {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.TITLE, "New Picture")
            values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera")
            this.imageUri =
                this.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, this.imageUri)
            this.startActivityForResult(
                takePictureIntent,
                1024
            )
        }
    }

    private fun pickImage() {
        val intent = Intent(Intent.ACTION_PICK, null)
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
        startActivityForResult(
            intent,
            2048
        )
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1024 && resultCode == RESULT_OK) {
            try {
                if (imageUri != null) {
                    val srcBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
                    val realPathFromURI: String? = FileUtil.getFilePathByUri(this, imageUri)
                    val i: Int =
                        readPictureDegree(
                            realPathFromURI
                        )
                    getCompressesBitmap = rotaingImageView(i, srcBitmap)
                    reloadAndDetectImage()
                }
            } catch (e: IOException) {

            }
        }
        if (requestCode == 2048 && resultCode == RESULT_OK) {
            data ?: return
            imageUri = data.data
            try {
                if (imageUri != null) {
                    val srcBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
                    val realPathFromURI: String? = FileUtil.getFilePathByUri(this, imageUri)
                    getCompressesBitmap = rotaingImageView(
                        readPictureDegree(
                            realPathFromURI
                        ), srcBitmap
                    )
                    reloadAndDetectImage()
                }
            } catch (e: IOException) {
                Logger.e(e)
            }
        }
    }

    private var analyzer: MLDocumentSkewCorrectionAnalyzer? = null
    private var corrected: Bitmap? = null
    private var imgCutResult: ImageView? = null

    private fun reloadAndDetectImage() {
        if (imageUri == null) {
            return
        }
        val createFrame = DocumentUtils.createFrame(getCompressesBitmap);
        DocumentUtils.analysisArray(createFrame, object :
            BlockingBaseObserver<Array<Point>>() {
            override fun onNext(t: Array<Point>) {
                setSource(getCompressesBitmap!!, t)
                setResult(t, createFrame)
            }

            override fun onError(e: Throwable) {
                Logger.e(e)
            }
        })
    }

    fun rotaingImageView(angle: Int, bitmap: Bitmap): Bitmap {
        val matrix = Matrix()
        matrix.postRotate(angle.toFloat())
        return Bitmap.createBitmap(
            bitmap, 0, 0,
            bitmap.width, bitmap.height, matrix, true
        )
    }

    fun readPictureDegree(path: String?): Int {
        var degree = 0
        try {
            val exifInterface = ExifInterface(path!!)
            val orientation = exifInterface.getAttributeInt(
                ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL
            )
            when (orientation) {
                ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90
                ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180
                ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270
            }
        } catch (e: IOException) {
            Log.e(
                "DocumentActivity",
                e.message!!
            )
        }
        return degree
    }

    fun setSource(bitmap: Bitmap, cropPoints: Array<Point>) {
        findViewById<CropImageView>(R.id.ivCrop)?.also {
            it.setImageBitmap(bitmap)
            it.cropPoints = cropPoints
        }
    }

    fun setResult(cropPoints: Array<Point>?, frame: MLFrame) {
        val points: MutableList<Point> = ArrayList()
        if (cropPoints != null) {
            points.add(cropPoints[0])
            points.add(cropPoints[1])
            points.add(cropPoints[2])
            points.add(cropPoints[3])
        }
        val coordinateData = MLDocumentSkewCorrectionCoordinateInput(points)
        getDetectdetectResult(coordinateData, frame)
    }

    private var correctionTask: Task<MLDocumentSkewCorrectionResult>? = null
    private fun getDetectdetectResult(
        coordinateData: MLDocumentSkewCorrectionCoordinateInput,
        frame: MLFrame
    ) {
        try {
            correctionTask = analyzer!!.asyncDocumentSkewCorrect(frame, coordinateData)
        } catch (e: Exception) {
            Log.e(
                "DocumentActivity",
                "The image does not meet the detection requirements."
            )
        }
        try {
            correctionTask?.addOnSuccessListener(OnSuccessListener<MLDocumentSkewCorrectionResult?> { refineResult -> // The check is successful.
                if (refineResult != null && refineResult.getResultCode() == 0) {
                    corrected = refineResult.getCorrected()
                    imgCutResult?.setImageBitmap(corrected)
                } else {
                    Toast.makeText(
                        this@DocumentActivity,
                        "The check fails.",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            })?.addOnFailureListener(OnFailureListener {
                Toast.makeText(
                    this@DocumentActivity,
                    "The check fails.",
                    Toast.LENGTH_SHORT
                ).show()
            })
        } catch (e: Exception) {
            Log.e(
                "DocumentActivity",
                "Please set an image."
            )
        }
    }
}
